package cn.bby.tools.dialog;

import cn.bby.tools.dialog.common.BaseResultDisplayDialog;
import cn.bby.tools.service.StateManagementService;
import cn.bby.tools.utils.ClassChooseUtil;
import cn.bby.tools.utils.ClassDataMockUtil;
import cn.bby.tools.utils.ClipboardUtil;
import cn.bby.tools.utils.FileStorageUtil;
import cn.bby.tools.utils.MessageUtil;
import cn.bby.tools.utils.TerminalUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.MD5;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.psi.PsiAnnotation;
import com.intellij.psi.PsiAnnotationMemberValue;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiParameter;
import com.intellij.psi.PsiParameterList;
import com.intellij.psi.PsiType;
import com.intellij.psi.util.PsiTypesUtil;
import com.intellij.util.ui.JBUI;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 * @description: GenCurlDialog
 * @author: bby
 * @date: 2025/10/21 17:13
 * @version: 1.0
 */
public class GenCurlDialog extends BaseResultDisplayDialog {
    private final PsiMethod method;
    private final JTextField prefixField = new JTextField(20);
    private final JTextField customHeaderField = new JTextField(20);
    private final JCheckBox mockCheckBox = new JCheckBox(MessageUtil.getMessage("message.Mock"));
    private final JCheckBox formatCheckBox = new JCheckBox(MessageUtil.getMessage("message.Format"));
    private final JCheckBox useTerminalCheckBox = new JCheckBox(MessageUtil.getMessage("message.UseTerminal"), true);
    private final JCheckBox saveRequestCheckBox = new JCheckBox("保存请求");
    private final JTextArea cmdResultTextArea = new JTextArea(5, 20);
    private final GenCurlState genCurlState = StateManagementService.getInstanceState().genCurlState;
    private final char wrap = '\"';
    private final JTextArea jsonBodyTextArea = new JTextArea(5, 20);

    /**
     * 构造函数
     *
     * @param project 项目实例
     * @param title   对话框标题
     */
    public GenCurlDialog(@Nullable Project project, String title, PsiMethod method) {
        super(project, title);
        this.method = method;
        setModal(false);
        init();
    }

    @Override
    protected @NotNull Action @NotNull [] createActions() {
        @NotNull Action[] actions = super.createActions();

        DialogWrapperAction copy = new DialogWrapperAction(MessageUtil.getMessage("message.Copy")) {
            @Override
            protected void doAction(ActionEvent e) {
                String result = resultTextArea.getText();
                if (result != null && !result.isEmpty()) {
                    ClipboardUtil.setTextToClipboard(result);
                }
            }
        };
        actions = new Action[]{copy, actions[0], actions[1]};
        return actions;
    }

    private void setResultText(String commandText, String jsonBodyText) {
        resultTextArea.setText(commandText);
        resultTextArea.setCaretPosition(0);
        jsonBodyTextArea.setText(jsonBodyText);
        jsonBodyTextArea.setCaretPosition(0);
    }

    private void sendRequest() {
        String curlCmd = resultTextArea.getText().replace("\n", "");
        curlCmd += " -d \"" + jsonBodyTextArea.getText()
                .replace("\n", "")
                .replace("\"", "\\\"") + "\"";
        try {
            String res = "";
            if (useTerminalCheckBox.isSelected()) {
                TerminalUtil.executeCommand(project, curlCmd);
            } else {
                res = TerminalUtil.executeCommandDirect(curlCmd);
                cmdResultTextArea.setText(res);
                cmdResultTextArea.setCaretPosition(res.lastIndexOf('\n') + 1);
            }

            if (saveRequestCheckBox.isSelected()) {
                saveReq(curlCmd);
            }
        } catch (Exception ex) {
            Messages.showErrorDialog(ex.getMessage(), "ERROR");
        }
    }

    private void saveReq(String curlCmd) {
        String relativePath = getMethodSavePath();
        JSONObject json = new JSONObject()
                .set("method", method.getName())
                .set("curlCmd", curlCmd)
                .set("data", jsonBodyTextArea.getText())
                .set("res", cmdResultTextArea.getText());
        FileStorageUtil.saveToFile(project, relativePath, json.toString());
    }

    private void loadReq() {
        String relativePath = getMethodSavePath();
        String str = FileStorageUtil.readFromFile(project, relativePath);
        if (!JSONUtil.isTypeJSONObject((str))) {
            return;
        }
        JSONObject json = JSONUtil.parseObj(str);
        if (!method.getName().equals(json.getStr("method"))) {
            return;
        }
        setResultText(json.getStr("curlCmd"), json.getStr("data"));
        cmdResultTextArea.setText(json.getStr("res"));
        cmdResultTextArea.setCaretPosition(0);
    }

    private String getMethodSavePath() {
        return "/HttpClient/" + MD5.create().digestHex(getMethodPath(method).toString()) + ".json";
    }

    @Override
    protected @Nullable JComponent createCenterPanel() {
        JPanel mainPanel = new JPanel(new BorderLayout());

        JPanel leftPanel = new JPanel(new GridBagLayout());
        leftPanel.setPreferredSize(new Dimension(300, 400));
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.insets = JBUI.insets(5);
        gbc.fill = GridBagConstraints.HORIZONTAL;
        gbc.weightx = 1.0;

        // 创建顶部控制面板
        JPanel topPanel = new JPanel(new FlowLayout(FlowLayout.LEADING));
        JLabel prefixLabel = new JLabel(MessageUtil.getMessageSuffix("message.Prefix"));
        topPanel.add(prefixLabel);
        topPanel.add(prefixField);

        gbc.gridx = 0;
        gbc.gridy = 0;
        gbc.weighty = 0;
        leftPanel.add(topPanel, gbc);

        // 添加自定义请求头面板
        JPanel headerPanel = new JPanel(new FlowLayout(FlowLayout.LEADING));
        JLabel customHeaderLabel = new JLabel(MessageUtil.getMessageSuffix("message.ReqHeader"));
        headerPanel.add(customHeaderLabel);
        headerPanel.add(customHeaderField);

        gbc.gridy = 1;
        gbc.weighty = 0;
        leftPanel.add(headerPanel, gbc);

        loadConfigurationFromState();

        // 生成面板
        gbc.gridy = 2;
        gbc.weighty = 0;
        JPanel genPanel = new JPanel(new GridBagLayout());
        GridBagConstraints genGbc = new GridBagConstraints();
        genGbc.insets = JBUI.insets(5);
        genGbc.fill = GridBagConstraints.HORIZONTAL;
        genGbc.weightx = 1.0;

        // 生成选项
        genGbc.gridx = 0;
        genGbc.gridy = 0;
        genGbc.weightx = 0;
        genPanel.add(new JLabel(MessageUtil.getMessage("message.Generate") + ":"), genGbc);

        genGbc.gridx = 1;
        genGbc.weightx = 0;
        genPanel.add(mockCheckBox, genGbc);

        genGbc.gridx = 2;
        genGbc.weightx = 0;
        genPanel.add(formatCheckBox, genGbc);

        genGbc.gridx = 3;
        genGbc.weightx = 1.0;
        genPanel.add(new JLabel(), genGbc);

        genGbc.gridx = 4;
        genGbc.weightx = 0;
        JButton genButton = new JButton(MessageUtil.getMessage("message.Generate") + "→");
        genButton.addActionListener(e -> {
            CurlCommandResult result = generateCurlCommand(method);
            setResultText(result.command, result.jsonBody);
        });
        genPanel.add(genButton, genGbc);
        leftPanel.add(genPanel, gbc);

        // 测试面板
        gbc.gridy = 3;
        gbc.weighty = 0;
        JPanel testPanel = new JPanel(new GridBagLayout());
        GridBagConstraints testGbc = new GridBagConstraints();
        testGbc.insets = JBUI.insets(5);
        testGbc.fill = GridBagConstraints.HORIZONTAL;
        testGbc.weightx = 1.0;

        // 测试选项
        testGbc.gridx = 0;
        testGbc.gridy = 0;
        testGbc.weightx = 0;
        testPanel.add(new JLabel(MessageUtil.getMessage("message.Test") + ":"), testGbc);

        testGbc.gridx = 1;
        testGbc.weightx = 0;
        testPanel.add(useTerminalCheckBox, testGbc);

        testGbc.gridx = 2;
        testGbc.weightx = 0;
        saveRequestCheckBox.setSelected(false);
        testPanel.add(saveRequestCheckBox, testGbc);

        testGbc.gridx = 3;
        testGbc.weightx = 1.0;
        testPanel.add(new JLabel(), testGbc);

        testGbc.gridx = 4;
        testGbc.weightx = 0;
        JButton testButton = new JButton(MessageUtil.getMessage("message.Test") + "↓");
        testButton.addActionListener(e -> sendRequest());
        testPanel.add(testButton, testGbc);
        leftPanel.add(testPanel, gbc);

        // cmd结果文本域
        cmdResultTextArea.setEditable(false);
        cmdResultTextArea.setBorder(BorderFactory.createLoweredBevelBorder());
        JScrollPane cmdScrollPane = new JScrollPane(cmdResultTextArea);
        cmdScrollPane.setBorder(BorderFactory.createTitledBorder(MessageUtil.getMessageSuffix("message.CmdResult")));

        gbc.gridy = 4;
        gbc.weighty = 1.0;
        gbc.fill = GridBagConstraints.BOTH;
        leftPanel.add(cmdScrollPane, gbc);

        // 右侧面板 - 输出框，使用上下分割面板
        JSplitPane rightSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
        rightSplitPane.setDividerSize(5);

        // 上部分显示命令
        resultTextArea.setText("");
        resultTextArea.setBorder(BorderFactory.createLoweredBevelBorder());
        resultTextArea.setEditable(true);
        JScrollPane commandScrollPane = new JScrollPane(resultTextArea);
        commandScrollPane.setBorder(BorderFactory.createTitledBorder(MessageUtil.getMessage("message.Command")));

        // 下部分显示JSON参数
        jsonBodyTextArea.setEditable(true);
        jsonBodyTextArea.setBorder(BorderFactory.createLoweredBevelBorder());
        JScrollPane jsonBodyScrollPane = new JScrollPane(jsonBodyTextArea);
        jsonBodyScrollPane.setBorder(BorderFactory.createTitledBorder(MessageUtil.getMessage("message.RequestBody")));

        rightSplitPane.setTopComponent(commandScrollPane);
        rightSplitPane.setBottomComponent(jsonBodyScrollPane);
        rightSplitPane.setDividerLocation(200);

        // 添加到主面板
        JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, leftPanel, rightSplitPane);
        splitPane.setDividerLocation(350);
        splitPane.setDividerSize(5);
        mainPanel.add(splitPane, BorderLayout.CENTER);

        loadReq();
        // 初始化内容
        if (StrUtil.isBlank(resultTextArea.getText())) {
            CurlCommandResult result = generateCurlCommand(method);
            setResultText(result.command, result.jsonBody);
        }
        return mainPanel;
    }

    private void loadConfigurationFromState() {
        prefixField.setText(genCurlState.prefixText);
        customHeaderField.setText(genCurlState.customHeaderText);
        mockCheckBox.setSelected(genCurlState.mockChecked);
        formatCheckBox.setSelected(genCurlState.formatChecked);
        useTerminalCheckBox.setSelected(genCurlState.useTerminalChecked);
    }

    private void saveConfigurationToState() {
        genCurlState.prefixText = prefixField.getText();
        genCurlState.customHeaderText = customHeaderField.getText();
        genCurlState.mockChecked = mockCheckBox.isSelected();
        genCurlState.formatChecked = formatCheckBox.isSelected();
        genCurlState.useTerminalChecked = useTerminalCheckBox.isSelected();
    }

    private CurlCommandResult generateCurlCommand(PsiMethod method) {
        saveConfigurationToState();
        try {
            PsiClass containingClass = method.getContainingClass();
            if (containingClass == null) {
                return new CurlCommandResult(MessageUtil.getMessage("message.ControllerNotFound"), "");
            }

            boolean isController = containingClass.hasAnnotation("org.springframework.stereotype.Controller") ||
                    containingClass.hasAnnotation("org.springframework.web.bind.annotation.RestController");

            if (!isController) {
                return new CurlCommandResult(MessageUtil.getMessage("message.NotController"), "");
            }

            String basePath = "";
            PsiAnnotation requestMapping = containingClass.getAnnotation("org.springframework.web.bind.annotation.RequestMapping");
            if (requestMapping != null) {
                basePath = getPath(requestMapping);
            }

            Result result = getMethodPath(method);

            if (basePath.endsWith("/")) {
                basePath = basePath.substring(0, basePath.length() - 1);
            }

            String fullPath = basePath;
            if (StrUtil.isNotBlank(result.methodPath)) {
                fullPath = basePath + (result.methodPath.startsWith("/") ? "" : "/") + result.methodPath;
            }
            if (!fullPath.startsWith("/")) {
                fullPath = "/" + fullPath;
            }

            List<String> queryParams = new ArrayList<>();
            String pathWithVariables = fullPath;

            PsiParameterList parameterList = method.getParameterList();
            PsiParameter bodyParam = null;

            for (PsiParameter parameter : parameterList.getParameters()) {
                PsiAnnotation pathVariable = parameter.getAnnotation("org.springframework.web.bind.annotation.PathVariable");
                if (pathVariable != null) {
                    String paramName = getParameterName(parameter, pathVariable);
                    pathWithVariables = pathWithVariables.replace("{" + paramName + "}", getSampleValue(parameter.getType()));
                    continue;
                }

                PsiAnnotation requestParam = parameter.getAnnotation("org.springframework.web.bind.annotation.RequestParam");
                if (requestParam != null) {
                    String paramName = getParameterName(parameter, requestParam);
                    queryParams.add(paramName + "=" + getSampleValue(parameter.getType()));
                    continue;
                }

                PsiAnnotation requestBody = parameter.getAnnotation("org.springframework.web.bind.annotation.RequestBody");
                if (requestBody != null) {
                    bodyParam = parameter;
                    continue;
                }

                queryParams.add(parameter.getName() + "=" + getSampleValue(parameter.getType()));
            }

            StringBuilder curlCommand = new StringBuilder();
            curlCommand.append("curl -X ").append(result.httpMethod).append(" ").append(wrap);

            curlCommand.append(prefixField.getText())
                    .append(pathWithVariables).append(wrap);

            if (!queryParams.isEmpty()) {
                curlCommand.append("?");
                for (int i = 0; i < queryParams.size(); i++) {
                    if (i > 0) {
                        curlCommand.append("&");
                    }
                    curlCommand.append(queryParams.get(i));
                }
            }

            String customHeader = customHeaderField.getText().trim();
            if (!customHeader.isEmpty()) {
                curlCommand.append("\n -H ").append(wrap).append(customHeader).append(wrap);
            }

            String jsonBody = "";
            if (("POST".equals(result.httpMethod) || "PUT".equals(result.httpMethod) || "PATCH".equals(result.httpMethod))
                    && bodyParam != null) {
                curlCommand.append("\n -H ")
                        .append(wrap)
                        .append("Content-Type: application/json")
                        .append(wrap);
                PsiType type = bodyParam.getType();
                PsiClass psiClass = getPsiClassFromBodyParam(method, type);

                String res = generateSampleDataForClass(psiClass, type, method);
                if (ClassChooseUtil.isCollType(method.getProject(), type)) {
                    res = "[" + res + "]";
                }
                jsonBody = res;
            }

            return new CurlCommandResult(curlCommand.toString(), jsonBody);
        } catch (Exception ex) {
            return new CurlCommandResult(MessageUtil.getMessage("message.GenerateCurlError") + ex.getMessage(), "");
        }
    }

    private String getParameterName(PsiParameter parameter, PsiAnnotation annotation) {
        String paramName = parameter.getName();
        PsiAnnotationMemberValue value = annotation.findAttributeValue("value");
        if (value == null || value.getText().replace("\"", "").isEmpty()) {
            value = annotation.findAttributeValue("name");
        }
        if (value != null && !value.getText().replace("\"", "").isEmpty()) {
            paramName = value.getText().replace("\"", "");
        }
        return paramName;
    }

    private PsiClass getPsiClassFromBodyParam(PsiMethod method, PsiType type) {
        boolean isColl = ClassChooseUtil.isCollType(method.getProject(), type);
        if (isColl) {
            return ClassChooseUtil.findCollClass(method.getProject(), type);
        } else {
            return PsiTypesUtil.getPsiClass(type);
        }
    }

    private String generateSampleDataForClass(PsiClass psiClass, PsiType type, PsiMethod method) {
        if (psiClass == null || psiClass.getQualifiedName() == null) {
            return "null";
        } else if (psiClass.getQualifiedName().startsWith("java.")) {
            return ObjectUtil.toString(ClassDataMockUtil.generateFieldValueInternal(
                    PsiTypesUtil.getClassType(psiClass),
                    new ClassDataMockUtil.GenMockParam(psiClass, mockCheckBox.isSelected()),
                    new HashMap<>()
            ));
        } else {
            JSONObject json = ClassDataMockUtil.generateMockData(psiClass, mockCheckBox.isSelected());
            return formatCheckBox.isSelected() ? json.toStringPretty() : json.toString();
        }
    }

    private static class CurlCommandResult {
        final String command;
        final String jsonBody;

        CurlCommandResult(String command, String jsonBody) {
            this.command = command;
            this.jsonBody = jsonBody;
        }
    }

    private Result getMethodPath(PsiMethod method) {
        String methodPath = "";
        String httpMethod = "GET";

        PsiAnnotation methodMapping = method.getAnnotation("org.springframework.web.bind.annotation.RequestMapping");
        if (methodMapping != null) {
            methodPath = getPath(methodMapping);

            PsiAnnotationMemberValue methodValue = methodMapping.findAttributeValue("method");
            if (methodValue != null) {
                String methodText = methodValue.getText();
                if (methodText.contains("POST")) {
                    httpMethod = "POST";
                } else if (methodText.contains("PUT")) {
                    httpMethod = "PUT";
                } else if (methodText.contains("DELETE")) {
                    httpMethod = "DELETE";
                } else if (methodText.contains("PATCH")) {
                    httpMethod = "PATCH";
                }
            }
        } else {
            if (method.hasAnnotation("org.springframework.web.bind.annotation.GetMapping")) {
                httpMethod = "GET";
                PsiAnnotation getMapping = method.getAnnotation("org.springframework.web.bind.annotation.GetMapping");
                methodPath = getPath(getMapping);
            } else if (method.hasAnnotation("org.springframework.web.bind.annotation.PostMapping")) {
                httpMethod = "POST";
                PsiAnnotation postMapping = method.getAnnotation("org.springframework.web.bind.annotation.PostMapping");
                methodPath = getPath(postMapping);
            } else if (method.hasAnnotation("org.springframework.web.bind.annotation.PutMapping")) {
                httpMethod = "PUT";
                PsiAnnotation putMapping = method.getAnnotation("org.springframework.web.bind.annotation.PutMapping");
                methodPath = getPath(putMapping);
            } else if (method.hasAnnotation("org.springframework.web.bind.annotation.DeleteMapping")) {
                httpMethod = "DELETE";
                PsiAnnotation deleteMapping = method.getAnnotation("org.springframework.web.bind.annotation.DeleteMapping");
                methodPath = getPath(deleteMapping);
            } else if (method.hasAnnotation("org.springframework.web.bind.annotation.PatchMapping")) {
                httpMethod = "PATCH";
                PsiAnnotation patchMapping = method.getAnnotation("org.springframework.web.bind.annotation.PatchMapping");
                methodPath = getPath(patchMapping);
            }
        }
        return new Result(methodPath, httpMethod);
    }

    private record Result(String methodPath, String httpMethod) {
    }

    private String getPath(PsiAnnotation mapping) {
        if (mapping == null) {
            return "";
        }
        PsiAnnotationMemberValue value = mapping.findAttributeValue("value");
        if (value != null) {
            return value.getText().replace("\"", "");
        } else {
            value = mapping.findAttributeValue("path");
            if (value != null) {
                return value.getText().replace("\"", "");
            }
        }
        return "";
    }

    private String getSampleValue(PsiType type) {
        ClassDataMockUtil.GenMockParam genMockParam = new ClassDataMockUtil.GenMockParam(method.getProject(), null,
                mockCheckBox.isSelected());
        genMockParam.stringRandom = false;
        Object res = ClassDataMockUtil.generateFieldValueInternal(type, genMockParam, new HashMap<>());
        return res == null ? "null" : res.toString();
    }

    public static class GenCurlState {
        public String prefixText = "http://localhost:8080";
        public String customHeaderText = "";
        public boolean mockChecked = false;
        public boolean formatChecked = false;
        public boolean useTerminalChecked = true;
    }
}